/**
 * 
 */
package com.vision.core.cl.ui;


import java.sql.Timestamp;
import java.text.ParseException;
import java.util.Date;
import java.util.List;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorDriver;
import com.google.gwt.editor.client.EditorError;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.sencha.gxt.core.client.Style.Side;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.Dialog;
import com.sencha.gxt.widget.core.client.Dialog.DialogMessages;
import com.sencha.gxt.widget.core.client.Dialog.PredefinedButton;
import com.sencha.gxt.widget.core.client.box.MessageBox;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.form.Field;
import com.sencha.gxt.widget.core.client.form.NumberPropertyEditor.DoublePropertyEditor;
import com.sencha.gxt.widget.core.client.form.PropertyEditor;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.tips.ToolTipConfig;
import com.vision.core.cm.util.DateUtils;

/**
 * @author Mark
 *
 */
public class UI {
	
	// Text %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Returns <tt>text</tt> wrapped with "&lt;font size='size'&gt;" tag.
	 * 
	 * @param size
	 * @param text
	 * @return
	 */
	public static String text(int size, String text) {
		return "<font size='" + size + "'>" + text + "</font>";
	}
	
	// Validators %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Returns true if the data in the form specified by <tt>driver</tt> is valid.
	 * Returns false otherwise. 
	 * 
	 * @param driver
	 * @return
	 */
	public static boolean isFormValid(EditorDriver<?> driver) {
		if (driver.hasErrors()) {
			// Find the first Editor that has an error and move the focus to it 
			List<EditorError> errors = driver.getErrors();
			for (EditorError error : errors) {
				final Editor<?> editor = error.getEditor();
				if (editor instanceof Field) {
					((Field<?>) editor).focus();
					break;
				}
			}
			return false;
		}
		return true;
	}
	
	// Convenience Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	// TODO Remove when we can find a way to include the loading gif via css
	public static void mask(Component component, String text) {
		if (component != null)
//			component.mask("<div style='border:0; text-align:center;'><img src='img/large-loading.gif'></div>" + text);
			component.mask(text);
	}

	public static String trimmedText(TextField text) {
		String v = text.getValue();
		if (v == null || v.isEmpty())
			return "";
		return v.trim();
	}
	
	public static ToolTipConfig tooltip(String title, String text) {
		ToolTipConfig config = new ToolTipConfig();
		if (title != null)
			config.setTitleHtml(title);  
	    config.setBodyHtml(text);  
	    config.setMouseOffset(new int[] {0, 0});  
	    config.setAnchor(Side.LEFT);
	    config.setMaxWidth(200);
	    return config;
	}
	
	// Dialogs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Shows an info dialog box.
	 * 
	 * @param header
	 * @param message
	 * @param handler
	 */
	public static void info(String header, String message, HideHandler handler) {
		MessageBox box = new MessageBox(header, message);
        box.setPredefinedButtons(PredefinedButton.OK);
        box.setIcon(MessageBox.ICONS.info());
        if (handler != null)
        	box.addHideHandler(handler);
        box.show();
	}
	
	/**
	 * Shows a warning dialog.
	 * 
	 * @param header
	 * @param message
	 * @param handler
	 */
	public static void warn(String header, String message, HideHandler handler) {
		MessageBox box = new MessageBox(header, message);
        box.setPredefinedButtons(PredefinedButton.OK);
        box.setIcon(MessageBox.ICONS.warning());
        if (handler != null)
        	box.addHideHandler(handler);
        box.show();
	}

	/**
	 * Shows a (Yes/No/Cancel) confirmation dialog with the specified parameters.
	 * 
	 * @param header
	 * @param message
	 * @param handler
	 */
	public static void confirm(String header, String message, HideHandler handler) {
		MessageBox box = new MessageBox(header, message);
        box.setPredefinedButtons(PredefinedButton.YES, PredefinedButton.NO, PredefinedButton.CANCEL);
        box.setIcon(MessageBox.ICONS.question());
        if (handler != null)
        	box.addHideHandler(handler);
        box.show();
	}
	
	public static void confirm(String header, String message, final IConfirmDialogHandler handler) {
		MessageBox box = new MessageBox(header, message);
        box.setPredefinedButtons(PredefinedButton.YES, PredefinedButton.NO, PredefinedButton.CANCEL);
        box.setIcon(MessageBox.ICONS.question());
        box.addHideHandler(new HideHandler() {
			@Override
			public void onHide(HideEvent event) {
				Dialog dialog = (Dialog) event.getSource();
				String text = dialog.getHideButton().getText();
				DialogMessages msgs = dialog.getDialogMessages();
				
				if (msgs.yes().equals(text)) {
					handler.onYes(event);
				} else if (msgs.no().equals(text)) {
					handler.onNo(event);
				} else if (msgs.cancel().equals(text)) {
					handler.onCancel(event);
				}
			}
		});
        box.show();
	}
	
	public static void confirmUnsavedChanges(final IConfirmDialogHandler handler) {
		confirm("Unsaved Changes", "There are unsaved changes. Would you like to continue loading?", handler);
	}
	
	// Field-related Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public static DoublePropertyEditor doubleEditor() {
		return new DoublePropertyEditor();
	}
	
	static final DateTimeFormat BASIC_DATE_FORMAT = DateTimeFormat.getFormat(DateUtils.BASIC_DATE_FORMAT);
	
	public static String basicDateFormat(Date date) {
		return BASIC_DATE_FORMAT.format(date);
	}
	
	public static PropertyEditor<Date> datePropertyEditor() {
		return new PropertyEditor<Date>() {
			@Override
			public String render(Date object) {
				return BASIC_DATE_FORMAT.format(object);
			}
			@Override
			public Date parse(CharSequence text) throws ParseException {
				return BASIC_DATE_FORMAT.parse(text.toString());
			}
		};
	}
	
	public static PropertyEditor<Date> dateEditor(String pattern) {
		final DateTimeFormat format = DateTimeFormat.getFormat(pattern);
		return new PropertyEditor<Date>() {
			@Override
			public String render(Date object) {
				return format.format(object);
			}
			@Override
			public Date parse(CharSequence text) throws ParseException {
				return format.parse(text.toString());
			}
		};
	}
	
	// Grid-related Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Calls {@link #column(ValueProvider, int, String, Cell)}, passing null to 
	 * cell parameter.
	 * 
	 * @param vp
	 * @param width
	 * @param header
	 * @return
	 */
	public static <O, T> ColumnConfig<O, T> column(ValueProvider<O, T> vp, int width, String header) {
		return column(vp, width, header, null);
	}
	
	/**
	 * Creates a {@link ColumnConfig} for the specified object O and type T with
	 * the specified {@link ValueProvider}, width, header, and cell.
	 * 
	 * @param vp
	 * @param width
	 * @param header
	 * @param cell
	 * @return
	 */
	public static <O, T> ColumnConfig<O, T> column(ValueProvider<O, T> vp, int width, String header, Cell<T> cell) {
		ColumnConfig<O, T> config = new ColumnConfig<O, T>(vp, width, header);
		if (cell != null)
			config.setCell(cell);
		return config;
	}
	
	public static <O, T> ColumnConfig<O, T> column(ValueProvider<O, T> vp, int width, String header, Cell<T> cell, HorizontalAlignmentConstant alignment) {
		ColumnConfig<O, T> config = column(vp, width, header, cell);
		config.setAlignment(alignment);
		return config;
	}
	
	public static AbstractCell<Date> dateCell() {
		return new AbstractCell<Date>() {
			@Override
			public void render(Context context, Date date, SafeHtmlBuilder sb) {
				sb.appendHtmlConstant(DateUtils.getBasicDateFormatted(date));
			}
		};
	}
	
	public static AbstractCell<Timestamp> timestampCell() {
		return new AbstractCell<Timestamp>() {
			@Override
			public void render(Context context, Timestamp timestamp, SafeHtmlBuilder sb) {
				sb.appendHtmlConstant(DateUtils.getBasicDateTimeFormatted(timestamp));
			}
		};
	}
	
	public static final NumberFormat AMOUNT_FORMAT = NumberFormat.getFormat("#,##0.00");
	
	public static final NumberFormat amountFormat() {
		return AMOUNT_FORMAT;
	}
	
	private static AbstractCell<Double> amountCell = null;
	
	// TODO Experiment performance between using this singleton instance of AbstractCell
	// versus using a new instance of AbstractCell
	public static AbstractCell<Double> amountCell() {
		if (amountCell == null)
			amountCell = decimalCell();
		return amountCell;
	}
	
	public static AbstractCell<Double> decimalCell() {
		return new AbstractCell<Double>() {
			@Override
			public void render(Context context, Double value, SafeHtmlBuilder sb) {
				if (value == null)
					sb.appendHtmlConstant("");
				else
					sb.appendHtmlConstant(AMOUNT_FORMAT.format(value));
			}
		};
	}
	
}